package org.hepeng.workx.spring.cloud.netflix.ribbon.loadbalancer;

import com.netflix.loadbalancer.Server;
import com.netflix.niws.loadbalancer.DiscoveryEnabledServer;
import com.netflix.zuul.context.RequestContext;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.hepeng.workx.spring.cloud.netflix.ribbon.RibbonRequestContext;
import org.hepeng.workx.web.util.HttpRequestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author he peng
 */

public class DebugRoutingRule extends AbstractThreadIsolationRule {

    private static final Logger LOG = LoggerFactory.getLogger(DebugRoutingRule.class);

    private static final String ROUTING_HOSTS_PROPERTY_NAME = "routingHosts";
    private static final List<String> ROUTING_HOSTS = new ArrayList<>();

    static {
        try {
            String LOCAL_HOST = InetAddress.getLocalHost().getHostAddress();
            String routingHosts = System.getProperty(ROUTING_HOSTS_PROPERTY_NAME , LOCAL_HOST).trim();
            if (StringUtils.isNotBlank(routingHosts)) {
                String[] routingHostArray = StringUtils.split(routingHosts, ",");
                if (ArrayUtils.isNotEmpty(routingHostArray)) {
                    for (String routingHost : routingHostArray) {
                        if (! ROUTING_HOSTS.contains(routingHost)) {
                            ROUTING_HOSTS.add(routingHost.trim());
                        }
                    }
                }
            }
            LOG.info("Exclude Developer Machines Debug Routing Hosts -> {} " , ROUTING_HOSTS);
        } catch (UnknownHostException e) {
            LOG.error("Get Deployment Machine Local Host Error " , e);
        }
    }

    @Override
    public List<Server> getParticipateLoadBalancingServers(Object key, RibbonRequestContext ribbonContext, List<Server> originalAllServers) {

        List<Server> loadBalancingServers = new ArrayList<>();
        List<Server> backupLoadBalancingServers = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(originalAllServers)) {
            RequestContext context = RequestContext.getCurrentContext();
            String clientIP = HttpRequestUtils.getClientIP(context.getRequest());
            LOG.info("Debug Routing Rule Remote Client IP -> {} " , clientIP);

            for (Server server : originalAllServers) {
                DiscoveryEnabledServer discoveryServer = (DiscoveryEnabledServer) server;
                Map<String, String> metadata = discoveryServer.getInstanceInfo().getMetadata();
                boolean debugRouteEnable = BooleanUtils.toBoolean(
                        metadata.getOrDefault("debug.route.enable" , Boolean.TRUE.toString()));
                String whitelist = metadata.getOrDefault("debug.route.ip.whitelist", "");
                Set<String> ipWhitelist = new HashSet<>();
                ipWhitelist.addAll(Arrays.asList(StringUtils.split(whitelist, ",")));

                if (debugRouteEnable) {
                    if (ipWhitelist.contains("*")) {
                        loadBalancingServers.add(server);
                    } else {
                        if (StringUtils.equals(clientIP , server.getHost()) && ! ipWhitelist.contains(clientIP)) {
                            ipWhitelist.add(clientIP);
                        }
                        if (CollectionUtils.isNotEmpty(ipWhitelist) && ipWhitelist.contains(clientIP)) {
                            LOG.info("Developer Client Request Routing Hit Server Host -> {} " , server.getHost());
                            loadBalancingServers.add(server);
                        }
                    }
                } else {
                    backupLoadBalancingServers.add(server);
                }

                if (ROUTING_HOSTS.contains(server.getHost()) && ! backupLoadBalancingServers.contains(server)) {
                    LOG.info("Exclude Developer Client Request Routing Hit Server Host -> {} " , server.getHost());
                    backupLoadBalancingServers.add(server);
                }
            }
        }

        if (CollectionUtils.isNotEmpty(loadBalancingServers)) {
            return loadBalancingServers;
        }

        resetAllServers(backupLoadBalancingServers);
        return backupLoadBalancingServers;
    }

    @Override
    protected boolean isSupported(Object key, RibbonRequestContext ribbonContext) {
        return true;
    }
}
